package clock.bean;

import java.awt.*;
import java.applet.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
import java.text.SimpleDateFormat;

public class ClockBean extends JPanel implements Runnable, Serializable
{
    private Thread threadClock; // thread of the clock
    private long now = -1;
    private boolean isAnalog = true; // true if analog clock is displayed
    private Font fontText = null; // font used to display digital clock
    private Color fontColor = Color.black; // font color;
    private Color hHandColor = Color.blue; // hour hand color
    private Color mHandColor = Color.blue; // minute hand color
    private Color sHandColor = Color.black; // second hand color
    private Color hPointColor = Color.red; // hour point color
    private Color mPointColor = Color.lightGray; // hour point color
    private String timeZone = TimeZone.getDefault().getID();
    private TimeZone timezone = TimeZone.getTimeZone(timeZone);
    private GregorianCalendar gregorianCalendar = new GregorianCalendar(timezone);
    private int xPoint[] = new int[4];
    private int yPoint[] = new int[4];

    private String strbackImage;
    private Image backImage; // background image
    private MediaTracker tracker = new MediaTracker (this);
    private Image imageBuffer; // buffer image

    private transient Vector actionListeners;
    private int timeTimer = -1;
    private int timeOutCurrent;
    private boolean hourChime;
    private boolean halfHourChime;
    private AudioClip audioClip;
    private SimpleDateFormat simpleDateFormatT;
    private SimpleDateFormat simpleDateFormatD;

    public ClockBean()
    {
	fontText = new Font ("Helvetica", Font.PLAIN, 16);
	audioClip = Applet.newAudioClip(ClockBean.class.getResource("bells.au"));
	simpleDateFormatD = new SimpleDateFormat("dd'-'MM'-'yyyy");
	simpleDateFormatT = new SimpleDateFormat("HH:mm:ss");
	try {
	    jbInit();
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
	start();
    }

    private void jbInit() throws Exception {
	this.addComponentListener(new java.awt.event.ComponentAdapter() {
	    public void componentResized(ComponentEvent e) {
		this_componentResized(e);
	    }
	});
	this.addMouseListener(new java.awt.event.MouseAdapter() {
	    public void mousePressed(MouseEvent e) {
		this_mousePressed(e);
	    }
	});
    }

    void this_mousePressed(MouseEvent e)
    {
	isAnalog = !isAnalog;
	//repaint();
    }

    // ----------------------------------------------------
    // init method, applet initialization
    // ----------------------------------------------------

    public void setFontText(Font fontText)
    {
	this.fontText = fontText;
	repaint();
    }
    public Font getFontText()
    {
	return this.fontText;

    }

    public void setFontColor(Color color)
    {
	fontColor = color;
	repaint();
    }
    public Color getFontColor()
    {
	return fontColor;
    }

    public void setHHandColor(Color color)
    {
	hHandColor = color;
	repaint();
    }
    public Color getHHandColor()
    {
	return hHandColor;
    }

    public void setMHandColor(Color color)
    {
	mHandColor = color;
	repaint();
    }
    public Color getMHandColor()
    {
	return mHandColor;
    }

    public void setSHandColor(Color color)
    {
	sHandColor = color;
	repaint();
    }
    public Color getSHandColor()
    {
	return sHandColor;
    }

    public void setHPointColor(Color color)
    {
	hPointColor = color;
	repaint();
    }
    public Color getHPointColor()
    {
	return hPointColor;
    }

    public void setMPointColor(Color color)
    {
	mPointColor = color;
	repaint();
    }
    public Color getMPointColor()
    {
	return mPointColor;
    }

    public void setBackImage(String strImage)
    {
	if ((strImage != null)&(strImage.length() > 0))
	{

	    backImage = getToolkit().getImage(ClockBean.class.getResource(strImage));
	    int w = backImage.getWidth(null);
	    int h = backImage.getHeight(null);
	    if ((w > 0) & (h > 0))
	    {
		strbackImage = strImage;
		if (backImage != null) tracker.removeImage(backImage);
		tracker.addImage (backImage, 0);
		repaint();
	    }
	}
	else
	{
	    strbackImage = null;
	    backImage = null;
	}
    }

    public String getBackImage()
    {
	return strbackImage;
    }

    public void setAnalog(boolean analog)
    {
	isAnalog = analog;
    }
    public boolean isAnalog()
    {
	return isAnalog;
    }

    public void setTimeOut(int s)
    {
	if (s > 0)
	{
	    timeTimer = s;
	    timeOutCurrent = 0;
	}
    }
    public int getTimeOut()
    {
	return timeTimer;
    }

    public void setHourChime(boolean on)
    {
	hourChime = on;
    }
    public boolean getHourChime()
    {
	return hourChime;
    }
    public void setHalfHourChime(boolean on)
    {
	halfHourChime = on;
    }
    public boolean getHalfHourChime()
    {
	return halfHourChime;
    }


//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    public void start ()
    {
	// Create the thread
	if (threadClock == null)
	{
	    threadClock = new Thread (this);
	    threadClock.setPriority(1);
	    threadClock.setName("threadClock");
	    threadClock.setDaemon(true);
	    threadClock.start ();
	}
    }

    public void stop ()
    {
	// Stop the thread
	if (threadClock != null)
	{
	    threadClock = null;
	    imageBuffer = null;
	}
	if(audioClip != null) audioClip.stop();
    }

    public void run ()
    {
	long time;
	// Wait until background image loaded
	try
	{
	    tracker.waitForAll ();
	}
	catch (InterruptedException e)
	{
	    return;
	}

	// If current thread is threadClock then ...
	while (Thread.currentThread () == threadClock)
	{
	    time = System.currentTimeMillis();
	    doTimerEvent();
	    now = time;
	    repaint ();
	    time = 1001 - (time % 1000);
	    try
	    {
		threadClock.sleep (time);
	    }
	    catch (InterruptedException e)
	    {
		break;
	    }
	}
    }

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    public void paint (Graphics g)
    {
	super.paint(g);
	Insets inset = this.getInsets();
	int wh = this.getWidth() - inset.left - inset.right;
	int hh = this.getHeight() - inset.top - inset.bottom;
	Dimension dim = new Dimension(wh, hh);
	Date newTime = new Date(now);
	gregorianCalendar.setTime(newTime);
	if (imageBuffer == null)
	{
	    imageBuffer = this.createImage (dim.width, dim.height);
	}

	int hour   =  gregorianCalendar.get(Calendar.HOUR_OF_DAY);//newTime.getHours ();
	int minute =  gregorianCalendar.get(Calendar.MINUTE);     //newTime.getMinutes ();
	int second = gregorianCalendar.get(Calendar.SECOND);      //newTime.getSeconds ();
	boolean doplay = ((second == 0) && (((minute == 0) && hourChime) || ((minute == 30) && halfHourChime)));
	if (doplay)
	{
	    if(audioClip != null)
	    {
		audioClip.play();
	    }
	}
	//if ((hour != oldHour) || (minute != oldMinute) ||(second != oldSecond))

	// Draw background in the buffer
	Graphics graphBuffer1 = imageBuffer.getGraphics ();
	Graphics2D graphBuffer = (Graphics2D)graphBuffer1;
	graphBuffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	DrawBackground (graphBuffer, dim);

	int centerX = dim.width >> 1;
	int centerY = dim.height >> 1;

	if (isAnalog)
	{
	    // Prepare the drawing of the analog clock

	    // Draw hour hand
	    double posHour = Math.PI * (hour / 6.0 + minute / 360.0);
	    double radius1 = Math.min (centerX, centerY) * 0.65;
	    double radius2 = Math.min (centerX, centerY) * 0.065;
	    xPoint[0] = (int)Math.round (centerX - 4 * radius2 * Math.sin (posHour));// - 1;
	    xPoint[1] = (int)Math.round (centerX - radius2 * Math.cos (posHour));
	    xPoint[2] = (int)Math.round (centerX + radius1 * Math.sin (posHour)) ;//+ 1;
	    xPoint[3] = (int)Math.round (centerX + radius2 * Math.cos (posHour));

	    yPoint[0] = (int)Math.round (centerY + 4 * radius2 * Math.cos (posHour));// - 1;
	    yPoint[1] = (int)Math.round (centerY - radius2 * Math.sin (posHour));
	    yPoint[2] = (int)Math.round (centerY - radius1 * Math.cos (posHour));// + 1;
	    yPoint[3] = (int)Math.round (centerY + radius2 * Math.sin (posHour));

	    graphBuffer.setColor (hHandColor);
	    graphBuffer.fillPolygon (xPoint, yPoint, 4);

	    if (((hour >= 0) && (hour <= 6)) || ((hour >= 12) && (hour <= 18)))
		graphBuffer.setColor (mHandColor.darker().darker());
	    else
		graphBuffer.setColor (Color.black);
	    graphBuffer.drawLine (xPoint[0], yPoint[0], xPoint[1], yPoint[1]);
	    graphBuffer.drawLine (xPoint[1], yPoint[1], xPoint[2], yPoint[2]);

	    if (((hour >= 0) && (hour <= 6)) || ((hour >= 12) && (hour <= 18)))
		graphBuffer.setColor (Color.black);
	    else
		graphBuffer.setColor (mHandColor.darker().darker());
	    graphBuffer.drawLine (xPoint[2], yPoint[2], xPoint[3], yPoint[3]);
	    graphBuffer.drawLine (xPoint[3], yPoint[3], xPoint[0], yPoint[0]);

	    radius1 = Math.min (centerX, centerY) * 0.80;
	    radius2 = Math.min (centerX, centerY) * 0.065;
	    // Draw minute hand
	    double posMinute = Math.PI * (minute / 30.0 + second / 1800.0);
	    xPoint[0] = (int)Math.round (centerX - 5 * radius2 * Math.sin (posMinute));// - 1;
	    xPoint[1] = (int)Math.round (centerX - radius2 * Math.cos (posMinute));
	    xPoint[2] = (int)Math.round (centerX + radius1 * Math.sin (posMinute));// + 1;
	    xPoint[3] = (int)Math.round (centerX + radius2 * Math.cos (posMinute));

	    yPoint[0] = (int)Math.round (centerY + 5 * radius2 * Math.cos (posMinute));// - 1;
	    yPoint[1] = (int)Math.round (centerY - radius2 * Math.sin (posMinute));
	    yPoint[2] = (int)Math.round (centerY - radius1 * Math.cos (posMinute));// + 1;
	    yPoint[3] = (int)Math.round (centerY + radius2 * Math.sin (posMinute));

	    graphBuffer.setColor (mHandColor);
	    graphBuffer.fillPolygon (xPoint, yPoint, 4);

	    if (minute < 30)
		graphBuffer.setColor (mHandColor.darker());//white);
	    else
		graphBuffer.setColor (Color.black);
	    graphBuffer.drawLine (xPoint[0], yPoint[0], xPoint[1], yPoint[1]);
	    graphBuffer.drawLine (xPoint[1], yPoint[1], xPoint[2], yPoint[2]);

	    if (minute < 30)
		graphBuffer.setColor (Color.black);
	    else
		graphBuffer.setColor (mHandColor.darker());//white);
	    graphBuffer.drawLine (xPoint[2], yPoint[2], xPoint[3], yPoint[3]);
	    graphBuffer.drawLine (xPoint[3], yPoint[3], xPoint[0], yPoint[0]);

	    // Draw second hand
	    radius1 = Math.min (centerX, centerY) * 0.80;
	    radius2 = Math.min (centerX, centerY) * 0.1;
	    double radius3 = Math.min (centerX, centerY) * 0.2;

	    double posSecond = Math.PI * second / 30.0;
	    graphBuffer.setColor (sHandColor);
	    int debSX =  (int)Math.round (centerX - radius3 * Math.sin (posSecond));
	    int debSY =  (int)Math.round (centerY + radius3 * Math.cos (posSecond));
	    int finSX =  (int)Math.round (centerX + radius1 * Math.sin (posSecond));
	    int finSY =  (int)Math.round (centerY - radius1 * Math.cos (posSecond));
	    graphBuffer.drawLine(debSX, debSY, finSX, finSY);

	    // le pivot
	    graphBuffer.setColor (Color.black);
	    graphBuffer.fillOval((int)Math.round ((centerX - radius2/2)), (int)Math.round ((centerY -radius2/2)), (int)Math.round (radius2), (int)Math.round (radius2));
	}
	else
	{
	    // Draw digital clock
	    graphBuffer.setFont (fontText);
	    graphBuffer.setColor (fontColor);
	    String displayDate = simpleDateFormatD.format(gregorianCalendar.getTime());
	    String displayTime = simpleDateFormatT.format(gregorianCalendar.getTime());
	    FontMetrics fm = graphBuffer.getFontMetrics ();
	    int espaceY = dim.height /3;
	    int orgDateY = espaceY;
	    int orgTimeY = 2 * espaceY;
	    int orgDateX = (dim.width - fm.stringWidth (displayDate)) /2;
	    int orgTimeX = (dim.width - fm.stringWidth (displayTime)) /2;
	    graphBuffer.drawString (displayDate, orgDateX, orgDateY);
	    graphBuffer.drawString (displayTime, orgTimeX, orgTimeY);
	}
	graphBuffer.dispose();
	// Copy the buffer to the "screen"
	g.drawImage (imageBuffer, inset.left, inset.top, null);
    }

    private void DrawBackground (Graphics g, Dimension dim)
    {
	g.setColor (this.getBackground());
	if(this.isOpaque())g.setColor (this.getBackground());
	else g.setColor(this.getParent().getBackground());
	g.fillRect (0, 0, dim.width, dim.height);

	// Draw background image
	boolean imageOK = false;
	if (backImage != null)
	{
	    int w = backImage.getWidth (this);
	    int h = backImage.getHeight (this);
	    imageOK = (w >0) & (h>0);
	}
	if (imageOK)
	{
	    int cote = Math.min(dim.width, dim.height);
	    g.drawImage (backImage, (dim.width - cote) >> 1, (dim.height - cote) >> 1, cote, cote, Color.white, this);
	}
	else
	{
	    if (isAnalog)
	    {
		int centerX = dim.width >> 1;
		int centerY = dim.height >> 1;
		double radius = Math.min (centerX, centerY) * 0.9;
		int d = (int)Math.round(radius/10D);
		// Draw hour
		for (int i = 1; i <= 12; i++)
		{
		    double buffer = Math.PI * (0.5 - i / 6.0);
		    int posX = (int)Math.floor (centerX + radius * Math.cos (buffer));
		    int posY = (int)Math.floor (centerY - radius * Math.sin (buffer));
		    g.setColor (hPointColor);
		    g.fill3DRect (posX - d/2, posY - d/2, d, d, true);
		}
		// Draw minute
		d = (int)Math.round(radius/16D);
		for (int i = 1; i <= 60; i++)
		{
		    if ((i % 5) != 0)
		    {
		       double buffer = Math.PI * i / 30.0;
		       int posX = (int)Math.floor (centerX + radius * Math.cos (buffer));
		       int posY = (int)Math.floor (centerY - radius * Math.sin (buffer));
		       g.setColor (mPointColor);
		       g.fill3DRect (posX - d/2, posY - d/2, d, d, false);
		    }
		}
	    }
	}
    }

    void this_componentResized(ComponentEvent e)
    {
	imageBuffer = null;
    }


//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    private void doTimerEvent()
    {
	if (timeTimer > 0)
	{
	    timeOutCurrent++;
	    if(timeOutCurrent == timeTimer)
	    {
		timeOutCurrent = 0;
		ActionEvent actionEvt = new ActionEvent(this, 0, "doTimer");
		fireActionPerformed(actionEvt);
	    }
	}
    }

    public synchronized void removeActionListener(ActionListener l)
    {
	if (actionListeners != null && actionListeners.contains(l))
	{
	    Vector v = (Vector) actionListeners.clone();
	    v.removeElement(l);
	    actionListeners = v;
	}
    }

    public synchronized void addActionListener(ActionListener l)
    {
	Vector v = actionListeners == null ? new Vector(2) : (Vector) actionListeners.clone();
	if (!v.contains(l))
	{
	    v.addElement(l);
	    actionListeners = v;
	}
    }

    protected void fireActionPerformed(ActionEvent e)
    {
	if (actionListeners != null)
	{
	    Vector listeners = actionListeners;
	    int count = listeners.size();
	    for (int i = 0; i < count; i++)
	    {
		((ActionListener) listeners.elementAt(i)).actionPerformed(e);
	    }
	}
    }

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    private void writeObject(ObjectOutputStream oos) throws IOException {
	oos.defaultWriteObject();
    }
    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
	ois.defaultReadObject();
    }


}